package com.shijie.sjoj.model.component;

import com.shijie.sjoj.model.base.BaseException;
import com.shijie.sjoj.model.utils.JsonFileUtils;
import com.shijie.sjoj.model.utils.StringUtils;
import lombok.Getter;

import java.util.*;

/**
 * 题目出入参类型格式化
 *
 * @author ZSJ
 */
@Getter
public class ParamsTypeFormat {

    /**
     * 参数的java类型
     */
    private final Class<?>[] types;

    /**
     * 参数的原字符串
     */
    private final String[] sources;

    /**
     * 字符串模板与java类型对应的模板匹配表
     */
    private static final Map<String, Class<?>> PATTERN_MATCH = new HashMap<>(Map.ofEntries(
            new ParamEntry<>("int", int.class),
            new ParamEntry<>("int[]", int[].class),
            new ParamEntry<>("long", long.class),
            new ParamEntry<>("long[]", long[].class),
            new ParamEntry<>("float", float.class),
            new ParamEntry<>("float[]", float[].class),
            new ParamEntry<>("double", double.class),
            new ParamEntry<>("double[]", double[].class),
            new ParamEntry<>("boolean", boolean.class),
            new ParamEntry<>("boolean[]", boolean[].class),
            new ParamEntry<>("char", char.class),
            new ParamEntry<>("char[]", char[].class),
            new ParamEntry<>("Integer", Integer.class),
            new ParamEntry<>("Integer[]", Integer[].class),
            new ParamEntry<>("Long", Long.class),
            new ParamEntry<>("Long[]", Long[].class),
            new ParamEntry<>("Float", Float.class),
            new ParamEntry<>("Float[]", Float[].class),
            new ParamEntry<>("Double", Double.class),
            new ParamEntry<>("Double[]", Double[].class),
            new ParamEntry<>("Boolean", Boolean.class),
            new ParamEntry<>("Boolean[]", Boolean[].class),
            new ParamEntry<>("Character", Character.class),
            new ParamEntry<>("Character[]", Character[].class),
            new ParamEntry<>("String", String.class),
            new ParamEntry<>("String[]", String[].class),
            new ParamEntry<>("List", List.class),
            new ParamEntry<>("ArrayList", ArrayList.class),
            new ParamEntry<>("Map", Map.class),
            new ParamEntry<>("HashMap", HashMap.class),
            new ParamEntry<>("Set", Set.class),
            new ParamEntry<>("HashSet", HashSet.class),
            new ParamEntry<>("Object", Object.class),
            new ParamEntry<>("Object[]", Object[].class)
    ));

    private ParamsTypeFormat(String[] paramsType) {
        this.sources = paramsType;
        Class<?>[] types = new Class<?>[paramsType.length];
        for (int i = 0; i < paramsType.length; i++) {
            types[i] = classFromStr(paramsType[i]);
        }
        this.types = types;
    }

    /**
     * 将参数的模板字符串数组格式化为ParamType对象
     *
     * @param paramsType ParamsTypeFormat
     * @return String[]
     */
    public static ParamsTypeFormat of(String[] paramsType) {
        return new ParamsTypeFormat(paramsType);
    }

    /**
     * 将参数的模板字符串集合格式化为ParamType对象
     *
     * @param paramsType ParamsTypeFormat
     * @return List
     */
    public static ParamsTypeFormat of(List<String> paramsType) {
        return new ParamsTypeFormat(paramsType.toArray(String[]::new));
    }

    /**
     * 由指定模板字符串得到java类
     *
     * @param pattern String
     * @return Class
     */
    public static Class<?> classFromStr(String pattern) {
        if (StringUtils.notBlank(pattern)) {
            Class<?> match = PATTERN_MATCH.get(pattern);
            if (null != match) {
                return match;
            }
            throw BaseException.throwError("未找到匹配的参数类型，转换失败：" + pattern);
        }
        throw BaseException.throwError("匹配字符串不能为null或其他无效的空白字符");
    }

    /**
     * 添加模式匹配（PATTERN_MATCH）键值对
     */
    public static void addPatternMatch(ParamEntry<?>... paramEntries) {
        for (ParamEntry<?> entry : paramEntries) {
            if (!PATTERN_MATCH.containsKey(entry.getKey())) {
                PATTERN_MATCH.put(entry.getKey(), entry.getValue());
            }
        }
    }

    /**
     * 参数类型格式化
     * 从文件中读取的数据，[]（数组/集合）一般读为集合类型（ArrayList），
     * 所以argv如果是[]一般集合
     */
    public static Object argvFormat(Object argv, Class<?> type) {
        // null校验
        if (null == argv || null == type) {
            throw BaseException.throwError("参数（argv）和类型（type）不能为null");
        }

        /*
        基本类型和包装类型不能用class.cast()转换
        */
        // 基本数据类型
        if (type.equals(int.class)) {
            return Integer.parseInt(argv.toString());
        } else if (type.equals(long.class)) {
            return Long.parseLong(argv.toString());
        } else if (type.equals(float.class)) {
            return Float.parseFloat(argv.toString());
        } else if (type.equals(double.class)) {
            return Double.parseDouble(argv.toString());
        } else if (type.equals(boolean.class)) {
            return Boolean.parseBoolean(argv.toString());
        } else if (type.equals(char.class)) {
            return argv.toString().charAt(0);
        }

        // 包装类型+String
        else if (type.equals(Integer.class)) {
            return Integer.valueOf(argv.toString());
        } else if (type.equals(Long.class)) {
            return Long.valueOf(argv.toString());
        } else if (type.equals(Float.class)) {
            return Float.valueOf(argv.toString());
        } else if (type.equals(Double.class)) {
            return Double.valueOf(argv.toString());
        } else if (type.equals(Boolean.class)) {
            return Boolean.valueOf(argv.toString());
        } else if (type.equals(Character.class)) {
            return argv.toString().charAt(0);
        } else if (type.equals(String.class)) {
            return argv.toString();
        }

        /*
        对于数字类型，toJson()和.toString结果一样，因为不涉及其他字符""的效果不明显
        而String类型则必须json化
        */
        // 数组/集合类型
        else if (type.equals(int[].class)) {
            return JsonFileUtils.fromJson1(argv.toString(), int[].class);
        } else if (type.equals(long[].class)) {
            return JsonFileUtils.fromJson1(argv.toString(), long[].class);
        } else if (type.equals(float[].class)) {
            return JsonFileUtils.fromJson1(argv.toString(), float[].class);
        } else if (type.equals(double[].class)) {
            return JsonFileUtils.fromJson1(argv.toString(), double[].class);
        } else if (type.equals(char[].class)) {
            return JsonFileUtils.fromJson1(argv.toString(), char[].class);
        } else if (type.equals(Integer[].class)) {
            return JsonFileUtils.fromJson1(argv.toString(), Integer[].class);
        } else if (type.equals(Float[].class)) {
            return JsonFileUtils.fromJson1(argv.toString(), Float[].class);
        } else if (type.equals(Long[].class)) {
            return JsonFileUtils.fromJson1(argv.toString(), Long[].class);
        } else if (type.equals(Double[].class)) {
            return JsonFileUtils.fromJson1(argv.toString(), Double[].class);
        } else if (type.equals(Character[].class)) {
            return JsonFileUtils.fromJson1(argv.toString(), Character[].class);
        } else if (type.equals(String[].class)) {
            //return JsonUtils.fromJson1(JsonUtils.toJson(argv), String[].class);
            return ((List<String>) argv).toArray(new String[0]);
        }

        // 其他类型
        return type.cast(argv);
    }


    /**
     * 模板字符串匹配java类键值对记录类
     *
     * @param pattern
     * @param match
     * @param <T>
     * @author ZSJ
     */
    public record ParamEntry<T>(String pattern, Class<T> match) implements Map.Entry<String, Class<T>> {

        @Override
        public String toString() {
            return "ParamEntry(" + pattern + " --> " + match.getCanonicalName() + ')';
        }

        @Override
        public String getKey() {
            return this.pattern;
        }

        @Override
        public Class<T> getValue() {
            return this.match;
        }

        /**
         * 实现Map.Entry接口的方法，但本类中为无效的方法，始终返回null
         *
         * @param value 无效的形参，因为返回值始终为null
         * @return null
         */
        @Deprecated
        @Override
        public Class<T> setValue(Class<T> value) {
            return null;
        }

    }


}